﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TribesPSO
{
    /// <summary>
    /// HyperspaceRandom is a wrapper for GaussianRandom that can generate vectors instead of scalars
    /// </summary>
    public class HyperspaceRandom : GaussianRandom, IHyperspaceRandom
    {
        /// <summary>
        /// Creates a new HyperspaceRandom seeded with the system time
        /// </summary>
        public HyperspaceRandom() : base() { }

        /// <summary>
        /// Creates a new HyperspaceRandom seeded with the default value
        /// </summary>
        /// <param name="seed">A number used to calculate a starting value for the pseudo-random number sequence</param>
        public HyperspaceRandom(int seed) : base(seed) { }

        /// <summary>
        /// Generates a Euclidian vector where each dimension is picked randomly based on a gaussian distrubion
        /// around the specified center and a sigma value of 1
        /// </summary>
        /// <param name="center"></param>
        /// <returns>A random euclidian vector</returns>
        public EuclidianVector NextGaussianVector(EuclidianVector center)
        {
            return NextGaussianVector(center, 1);
        }

        /// <summary>
        /// Generates a Euclidian vector where each dimension is picked randomly based on a gaussian distribution
        /// around a center
        /// </summary>
        /// <param name="center"></param>
        /// <param name="sigma"></param>
        /// <returns>A random euclidian vector</returns>
        public EuclidianVector NextGaussianVector(EuclidianVector center, double sigma)
        {
            /*Original more readable code with an extra euclidian vector*/
            //if (center == null)
            //{
            //    throw new ArgumentNullException("center");
            //}
            //List<double> GaussianDimensions = new List<double>();
            //for (int n = 0; n < center.Dimensions; n++)
            //{
            //    GaussianDimensions.Add(this.NextGaussian(0, sigma));
            //}
            //EuclidianVector noise = new EuclidianVector(GaussianDimensions);
            //return center + noise;

            /*Faster code.  Gets rid of a list allocation in the EuclidianVector constructor */
            if (ReferenceEquals(center, null))
            {
                throw new ArgumentNullException("center");
            }
            EuclidianVectorBuilder GaussianDimensions = new EuclidianVectorBuilder();
            for (int n = 0; n < center.Dimensions; n++)
            {
                GaussianDimensions.AddDimensionData(center[n] + this.NextGaussian(0, sigma));
            }
            return GaussianDimensions.ToEuclidianVector();
        }

        /// <summary>
        /// Using a uniform distribution, generates a euclidian vector in a hypersphere with a
        /// specified center and radius
        /// </summary>
        /// <param name="center">The center of the hypersphere</param>
        /// <param name="radius">The radius of the hypersphere</param>
        /// <returns></returns>
        public EuclidianVector NextUniformVector(EuclidianVector center, double radius)
        {
            /*Original more readable code with an extra euclidian vector construction*/
            //if (center == null)
            //{
            //    throw new ArgumentNullException("center");
            //}
            ////Generate a vector that points in a random direction and normalize it
            //EuclidianVector RawVector = this.NextGaussianVector(EuclidianVector.Origin(center.Dimensions));
            //EuclidianVector NormalizedVector = RawVector / RawVector.Length;

            ////Compute a length for the vector between 0 and radius
            //double randomRadius = base.NextDouble() * radius;

            //return center + (NormalizedVector * randomRadius);

            /* Faster code */
            if (ReferenceEquals(center, null))
            {
                throw new ArgumentNullException("center");
            }
            //Generate a vector that points in a random direction and normalize it
            EuclidianVector RawVector = this.NextGaussianVector(EuclidianVector.Origin(center.Dimensions));

            //Compute a length for the vector between 0 and radius
            double randomRadius = base.NextDouble() * radius;

            //Return a vector that points in the direction of the RawVector, but with the radius we just computed

            return center + (RawVector * (randomRadius / RawVector.Length));
        }
    }
}
